﻿using System;
using System.Text;
using log4net.Util;
using log4net.Core;
using System.Data;
using System.IO;
using LogHelper.SQLiteORM.Generic;
using System.Data.SQLite;

namespace LogHelper.Appender
{
    public class SQLiteORMAppender : log4net.Appender.BufferingAppenderSkeleton
    {
        public SQLiteORMAppender()
        {
            ConnectionType = "System.Data.SQLite.SQLiteConnection, System.Data.SQLite, Version=1.0.104.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139";
            DbFileMode = "static";
            ReconnectOnError = false;
            DbPath = AppDomain.CurrentDomain.BaseDirectory + $"log";
            ConnectionString = "";
        }
        //发生错误是否自动重新连接数据库
        public bool ReconnectOnError { get; set; }
        //数据库文件模式：static:静态文件，需指定ConnectionString；month:按月生成文件，需指定文件夹。默认为static
        public string DbFileMode { get; set; }
        //DbFileMode为static时需指定连接串，为month时动态生成
        public string ConnectionString { get; set; }
        //DbFileMode为month时需指定存放文件夹，不指定则默认为程序根目录的log文件
        public string DbPath { get; set; }
        public string ConnectionType { get; set; }

        public SecurityContext SecurityContext { get; set; }

        private readonly static Type declaringType = typeof(SQLiteORMAppender);
        protected SQLiteConnection Connection { get; set; }

        override public void ActivateOptions()
        {
            base.ActivateOptions();
            
            if (SecurityContext == null)
            {
                SecurityContext = SecurityContextProvider.DefaultProvider.CreateSecurityContext(this);
            }
            if (DbFileMode == "month")
            {
                DynamicConn();
            }
            else
            {
                InitializeDatabaseConnection();
            }
        }

        override protected void OnClose()
        {
            base.OnClose();
            DiposeConnection();
        }

        //根据月份生成数据库
        private void DynamicConn()
        {
            if (DbFileMode == "month")
            {
                if (!Directory.Exists(DbPath))
                {
                    Directory.CreateDirectory(DbPath);
                }

                string _connectString = $"Data Source={DbPath + $"\\{DateTime.Now.ToString("yyMM")}.db"};";
                //如果是新的月份，更新连接串
                if (ConnectionString != _connectString || Connection == null)
                {
                    if (Connection != null && Connection.State == ConnectionState.Open)
                    {
                        DiposeConnection();
                    }
                    ConnectionString = _connectString;
                    InitializeDatabaseConnection();
                }
            }

        }

        override protected void SendBuffer(LoggingEvent[] events)
        {

            DynamicConn();
            if (ReconnectOnError && (Connection == null || Connection.State != ConnectionState.Open))
            {
                LogLog.Debug(declaringType, "Attempting to reconnect to database. Current Connection State: " + ((Connection == null) ? SystemInfo.NullText : Connection.State.ToString()));

                InitializeDatabaseConnection();
            }

            //检查连接是否存在并且是打开状态
            if (Connection != null && Connection.State == ConnectionState.Open)
            {
                //创建事务
                using (IDbTransaction dbTran = Connection.BeginTransaction())
                {
                    try
                    {
                        SendBuffer(dbTran, events);

                        //提交事务
                        dbTran.Commit();
                    }
                    catch (Exception ex)
                    {
                        //出错时，回滚
                        try
                        {
                            dbTran.Rollback();
                        }
                        catch (Exception)
                        {
                            
                        }

                        //不能写入数据库
                        ErrorHandler.Error("Exception while writing to database", ex);
                    }
                }
            }
        }

        virtual protected void SendBuffer(IDbTransaction dbTran, LoggingEvent[] events)
        {
            //获得消息对象，开始ORM转换，写入数据库
            StringBuilder sbSql = new StringBuilder();
            foreach (var e in events)
            {
                var sql = new SqLite(Connection);
                sql.Initialize(e.MessageObject.GetType());
                sql.Insert(e.MessageObject);
            }

        }
        virtual protected SQLiteConnection CreateConnection(Type connectionType, string connectionString)
        {
            SQLiteConnection connection = (SQLiteConnection)Activator.CreateInstance(connectionType);
            connection.ConnectionString = connectionString;
            return connection;
        }

        virtual protected string ResolveConnectionString(out string connectionStringContext)
        {
            if (ConnectionString != null && ConnectionString.Length > 0)
            {
                connectionStringContext = "ConnectionString";
                return ConnectionString;
            }

            connectionStringContext = "Unable to resolve connection string from ConnectionString, ConnectionStrings.";
            return string.Empty;
        }

        virtual protected Type ResolveConnectionType()
        {
            try
            {
                return SystemInfo.GetTypeFromString(ConnectionType, true, false);
            }
            catch (Exception ex)
            {
                ErrorHandler.Error("Failed to load connection type [" + ConnectionType + "]", ex);
                throw;
            }
        }

        private void InitializeDatabaseConnection()
        {
            string connectionStringContext = "Unable to determine connection string context.";
            string resolvedConnectionString = string.Empty;

            try
            {
                DiposeConnection();

                //设置连接串
                resolvedConnectionString = ResolveConnectionString(out connectionStringContext);

                Connection = CreateConnection(ResolveConnectionType(), resolvedConnectionString);

                using (SecurityContext.Impersonate(this))
                {
                    //打开数据库连接
                    Connection.Open();
                }
            }
            catch (Exception e)
            {
                //无法连接数据库
                ErrorHandler.Error("Could not open database connection [" + resolvedConnectionString + "]. Connection string context [" + connectionStringContext + "].", e);

                Connection = null;
            }
        }

        private void DiposeConnection()
        {
            if (Connection != null)
            {
                try
                {
                    Connection.Close();
                }
                catch (Exception ex)
                {
                    LogLog.Warn(declaringType, "Exception while disposing cached connection object", ex);
                }
                Connection = null;
            }
        }


    }
}
